/****************
  Hook up a shift register controlled LED Matrix to the ADAFruite GFX library.

  I would recommend using something like the HT16K33 or HT1632C
  controller if you can get one. If not, an appropriate shift register
  can do the job, but will require the MCU handle the multiplexing.
*******/

#include "MWM_GFX.h"
#include <TimerOne.h>

// For now, we can only run one of these at a time. This sucks, but
// running on the interrupt handler makes having more than one a bit
// complicated. So for now...
static uint8_t red[8], green[9], com[8] ;
static uint8_t dataPin, clockPin, latchPin ;

static void
handler() {
  static uint8_t i = 0 ;

  digitalWrite(latchPin, LOW) ;
  shiftOut(dataPin, clockPin, LSBFIRST, green[i]) ;
  shiftOut(dataPin, clockPin, LSBFIRST, red[i]) ;
  shiftOut(dataPin, clockPin, LSBFIRST, com[i]) ;
  digitalWrite(latchPin, HIGH) ;
  i = (i + 1) & 0x07 ;
}

MWM_74595::MWM_74595(void) {
  constructor(8, 8) ;
}  


void
MWM_74595::begin(uint8_t dataPin_, uint8_t clockPin_, uint8_t latchPin_) {
  dataPin = dataPin_ ;
  clockPin = clockPin_ ;
  latchPin = latchPin_ ;

  // Default to common anode type.
  common(ANODE) ;

  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
  digitalWrite(clockPin, LOW);
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, 255); // clear all 16 output bits
  shiftOut(dataPin, clockPin, LSBFIRST, 255); // 8 bits at a time.
  shiftOut(dataPin, clockPin, LSBFIRST, 0);  // And now the common bits.
  digitalWrite(latchPin, HIGH);

  // And now kick off the timer.
  Timer1.initialize(2000) ;
  Timer1.attachInterrupt(handler) ;
}
  
// Cribbed from ADAFruint_BicolorMatrix
void
MWM_74595::drawPixel(int16_t x, int16_t y, uint16_t color) {
  if ((y < 0) || (y >= 8)) return;
  if ((x < 0) || (x >= 8)) return;

  switch (getRotation()) {
  case 1:
    swap(x, y);
    x = 8 - x - 1;
    break;
  case 2:
    x = 8 - x - 1;
    y = 8 - y - 1;
    break;
  case 3:
    swap(x, y);
    y = 8 - y - 1;
    break;
  }

  /*
   * This behaves differently than the ADAFruit Bicolor Matrix. We
   * turn off the LEDs, then turn the red & green ones on as
   * appropriate. The ADAFRuit version doesn't turn the LED's off
   * unless you specify off, which means that if you set an LED to
   * red, then later set it to green, you get yellow. I believe that
   * behavior is a bug, and so don't copy it.
   */

  uint8_t bit = 1 << x ;

  redBuffer[y] &= ~bit ;
  greenBuffer[y] &= ~bit ;
  if (color & LED_GREEN) {
    greenBuffer[y] |= bit ;
  }
  if (color & LED_RED) {
    redBuffer[y] |= bit ;
  } 
  // LED_YELLOW is defined as LED_GREEN | LED_YELLOW, so is correct here.
}
  
void
MWM_74595::clear(void) {
  for (uint8_t i = 0; i < 8; i++) {
    redBuffer[i] = greenBuffer[i] = 0 ;
  }
}

void
MWM_74595::writeDisplay(void) {
  Timer1.stop() ;
  for (uint8_t i = 0; i < 8; i++) {
    if (anode) {
      red[i] = ~redBuffer[i] ;
      green[i] = ~greenBuffer[i] ;
    } else {
      red[i] = redBuffer[i] ;
      green[i] = greenBuffer[i] ;
    }
  }
  Timer1.resume() ;
}

void
MWM_74595::common(uint8_t type) {
  anode = type == ANODE ;

  // Precompute the common values for the interrupt handler.
  for (uint8_t i = 0, c = 0x80; i < 8; i += 1, c >>= 1) {
     com[i] = anode ? c : ~c ;
  }
}

void
MWM_74595::commDelay(long newDelay) {
  Timer1.setPeriod(newDelay) ;
}
